﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace BaoJianSoft.Common.Data
{
	public sealed class TransactionScopeContext
	{
		private static readonly Dictionary<int, int> transactionReferences = new Dictionary<int, int>();

		/// <summary>
		/// The callback will be invoked after a new transaction for current executing thread been started.
		/// </summary>
		public static Action<int> AfterNewTransactionStarted;

		/// <summary>
		/// The callback will be invoked after the transaction for current executing thread been committed.
		/// </summary>
		public static Action<int> AfterTransactionCommitted;

		/// <summary>
		/// The callback will be invoked after the transaction for current executing thread been rolled back.
		/// </summary>
		public static Action<int> AfterTransactionRollback;

		/// <summary>
		/// Gets whether the executing thread is in a transaction.
		/// </summary>
		/// <param name="threadId"></param>
		/// <returns></returns>
		internal static bool IsInTransaction(int threadId)
		{
			return transactionReferences.ContainsKey(threadId) && transactionReferences[threadId] > 0;
		}

		/// <summary>
		/// Increase the transaction reference for current executing thread.
		/// </summary>
		internal static void Increase()
		{
			if (!transactionReferences.ContainsKey(Thread.CurrentThread.ManagedThreadId))
			{
				transactionReferences[Thread.CurrentThread.ManagedThreadId] = 1;
				if (AfterNewTransactionStarted != null)
					AfterNewTransactionStarted(Thread.CurrentThread.ManagedThreadId);
			}
			else
				transactionReferences[Thread.CurrentThread.ManagedThreadId] = transactionReferences[Thread.CurrentThread.ManagedThreadId] + 1;
		}

		/// <summary>
		/// Decrease the transaction reference for current executing thread. 
		/// The callback "" will be invoked when the reference decreased to zero.
		/// </summary>
		/// <exception cref="InvalidProgramException">If there is no transaction available for current executing thread.</exception>
		internal static void Decrease()
		{
			if (!transactionReferences.ContainsKey(Thread.CurrentThread.ManagedThreadId))
				return;

			int refCount = transactionReferences[Thread.CurrentThread.ManagedThreadId] - 1;
			if (refCount > 0)
			{
				transactionReferences[Thread.CurrentThread.ManagedThreadId] = refCount;
				return;
			}

			transactionReferences.Remove(Thread.CurrentThread.ManagedThreadId);
			if (AfterTransactionCommitted != null)
				AfterTransactionCommitted(Thread.CurrentThread.ManagedThreadId);
		}

		/// <summary>
		/// Rollback the transaction for current executing thread.
		/// </summary>
		internal static void Rollback()
		{
			transactionReferences.Remove(Thread.CurrentThread.ManagedThreadId);
			if (AfterTransactionRollback != null)
				AfterTransactionRollback(Thread.CurrentThread.ManagedThreadId);
		}
	}
}
